﻿#region Copyright Info

// 
// Majesty of Omega - The next evolution of macro management space strategy
// 
// Copyright 2007-2009 André Claaßen
// 
// This file is protected by the terms and conditions of the
// Microsoft Community License (Ms-CL 1.1), a copy of which should
// have been distributed along with this software. If not,
// you may find the license information at the following URL:
// 
// http://www.microsoft.com/resources/sharedsource/licensingbasics/communitylicense.mspx

#endregion

#region

using System;
using System.Collections.Generic;
using System.Linq;
using Majesty_of_Omega.Config;
using DesignByContract;

#endregion

namespace Majesty_of_Omega.Model.Research
{
    /// <summary>
    /// The Technology Research class stores the current research level for a technology field (<see cref="TechField" />)
    /// </summary>
    [Serializable]
    public class TechFieldResearch : ResearchItem
    {
        /// <summary>
        /// This flag indicates, if this technology field is already researched
        /// </summary>
        private bool researched;

        /// <summary>
        /// The corresponding Technology Field for this research item
        /// </summary>
        [NonSerialized] private TechField techField;

        private readonly ResearchManager researchManager;
        private int progressInCurrentLevel;

        /// <summary>
        /// Gets the research manager.
        /// </summary>
        /// <value>The research manager.</value>
        public ResearchManager ResearchManager
        {
            get { return researchManager; }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TechFieldResearch"/> class.
        /// </summary>
        /// <remarks>
        /// All technology fields, with no field dependencies, are automatically researched
        /// </remarks>
        /// <param name="researchManager">The associated research manager</param>
        /// <param name="field">The technology field.</param>
        public TechFieldResearch(ResearchManager researchManager, TechField field)
            : base(field.Type_ID)
        {
            this.researchManager = researchManager;
            this.techField = field;
            this.researched = field.Field_Dependencies.Length == 0;
        }

        /// <summary>
        /// This flag indicates, if this technology field is already researched
        /// </summary>
        public bool Researched
        {
            get { return researched; }
            set { researched = value; }
        }

        /// <summary>
        /// Gets or sets the tech field for this research item
        /// </summary>
        /// <value>The tech field.</value>
        public TechField TechField
        {
            get
            {
                if (techField == null)
                {
                    techField = SpreadSheetManager.TechTables.TechFieldDictionary[ResearchItemTypeID];
                }
                return techField;
            }
        }

        /// <summary>
        /// Calculates the progress of researching the current level in percent.
        /// </summary>
        /// <value>The progress in current level.</value>
        /// <remarks>
        /// 70% means, that you must research 30% to enter the next level.
        /// </remarks>
        public override int ProgressInCurrentLevel
        {
            get
            {
                return progressInCurrentLevel;
            }
        }

        /// <summary>
        /// Gets the icon of this research item.
        /// </summary>
        /// <value>The icon.</value>
        public override string Icon
        {
            get 
            {
                return techField.IconName;
            }
        }

        /// <summary>
        /// Gets or sets the tech level of the current research
        /// </summary>
        /// <value>The tech level.</value>
        public override int TechLevel { get; set; }

        private int researchPointsAccumulated;

        /// <summary>
        /// Gets or sets the accumulated research points for the the technology level
        /// </summary>
        /// <value>The research points accumulated.</value>
        public int ResearchPointsAccumulated
        {
            get { return researchPointsAccumulated; }
            set
            {
                Require.IsTrue(value >= 0,
                               "The value for the research points must be greater than 0. Negative values indicate calculation errors");
                researchPointsAccumulated = value;
            }
        }

        /// <summary>
        /// Breaktrough. If the accumulated research points are greater than this value, a new 
        /// tech level is reached
        /// </summary>
        public int ResearchBreakThrough { get; set; }

        /// <summary>
        /// Increases the tech level.
        /// </summary>
        public void IncreaseTechLevel()
        {
            while (this.ResearchPointsAccumulated >= this.ThresholdForNextLevel)
            {
                this.ResearchPointsAccumulated -= this.ThresholdForNextLevel;
                this.TechLevel++;
            }
            Researched = true;
        }
 
        /// <summary>
        /// Unlocks the tech field.
        /// </summary>
        public void UnlockTechField()
        {
            TechLevel =
                TechField.TechFieldDependencies.Min(td => td.TechLevel);
            Researched = true;
        }
 
        #region Equal functions

        /// <summary>
        /// Equalses the specified obj.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <returns></returns>
        public bool Equals(TechFieldResearch obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            return base.Equals(obj) &&
                   obj.researched.Equals(researched) &&
                   obj.TechLevel == TechLevel &&
                   obj.ResearchPointsAccumulated == ResearchPointsAccumulated &&
                   obj.ResearchBreakThrough == ResearchBreakThrough;
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">
        /// The <paramref name="obj"/> parameter is null.
        /// </exception>
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            return Equals(obj as TechFieldResearch);
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            unchecked
            {
                int result = base.GetHashCode();
                result = (result*397) ^ researched.GetHashCode();
                result = (result*397) ^ TechLevel;
                result = (result*397) ^ ResearchPointsAccumulated;
                result = (result*397) ^ ResearchBreakThrough;
                return result;
            }
        }

        #endregion

        /// <summary>
        /// Gets the available technoly applications for the given level.
        /// </summary>
        /// <param name="level">The technology level.</param>
        /// <returns></returns>
        public IEnumerable<TechApplicationStateInfo> GetTechApplicationsForLevel(int level)
        {
            Check<PreconditionException>.IsTrue(level >= 0);
            List<TechApplicationStateInfo> infos = this.researchManager.GetTechApplicationStateForFieldAndLevel(
                        this.techField, 
                        level, 
                        ResearchManager.TechApplicationState.GetAvailableTechApps);
            Check<PostconditionException>.IsNotNull(infos);
            return infos;
        }

        /// <summary>
        /// Calculates the Threshold value for entering the next technology level
        /// </summary>
        /// <value>The threshold for next level.</value>
        public int ThresholdForNextLevel
        {
            get
            {
                return TechField.CalculateResearchPointsForLevel(TechLevel + 1) -
                       TechField.CalculateResearchPointsForLevel(TechLevel); 
            }
        }


        /// <summary>
        /// Calculates the progress of researching the current techlevel in percent.
        /// </summary>
        /// <returns>Progress in the current techlevel in percent</returns>
        public void RecalculateProgressInPercent()
        {
            int thresHoldForNextLevel = this.ThresholdForNextLevel;
            Require.IsTrue(this.ResearchPointsAccumulated>= 0, "Wrong calulated remaining research points. The researchpoints are below the current tech level" );

            this.progressInCurrentLevel =  this.ResearchPointsAccumulated*100/thresHoldForNextLevel;
            Ensure.IsTrue( this.progressInCurrentLevel >= 0  && this.progressInCurrentLevel <= 100, "The allowed progress must lie between 0 and 100 percent");
        }
    }
}